/*
日志器模块
1. 抽象一个日志器类
2. 派生出（同步、异步）日志器类

Logger类用于记录日志信息，并提供接口供用户记录不同等级的日志；
LoggerBulider 类用于构建日志器对象；
ResetLogger 类用于重设日志器参数；
LoggerManager 类用于管理日志器对象, 其自身是懒汉版本的单例模式；

*/


#ifndef LOGGER_HPP
#define LOGGER_HPP

#include "util.hpp"
#include "level.hpp"
#include "format.hpp"
#include "sink.hpp"
#include "looper.hpp"

#include <mutex>
#include <memory>
#include <atomic>
#include <vector>
#include <cstdarg>
#include <unordered_map>

namespace xzt
{
    namespace log
    {
        class Logger
        {
        public:
            using ptr = std::shared_ptr<Logger>;

            friend class ResetLogger;  // 声明用于重设日志器参数的类为Logger的友元类

            Logger(const std::string& loggerName,
                   Level::value level,
                   const Formatter::ptr& formatter,
                   const std::vector<LogSink::ptr>& sinks)
                : _loggerName(loggerName), _limitLevel(level), _formatter(formatter), _sinks(sinks) {}
            
            std::string getLoggerName() const
            {
                return _loggerName;
            }

            // 为不同等级的日志提供接口
            template<typename ...Args>
            void _debug(const std::string& filename, size_t line, const std::string& fmt, Args&& ...arg)
            {
                doLog(Level::value::DEBUG, filename, line, fmt, std::forward<Args>(arg)...);
            }

            template<typename ...Args>
            void _info(const std::string& filename, size_t line, const std::string& fmt, Args&& ...arg)
            {
                doLog(Level::value::INFO, filename, line, fmt, std::forward<Args>(arg)...);
            }

            template<typename ...Args>
            void _warn(const std::string& filename, size_t line, const std::string& fmt, Args&& ...arg)
            {
                doLog(Level::value::WARN, filename, line, fmt, std::forward<Args>(arg)...);
            }

            template<typename ...Args>
            void _error(const std::string& filename, size_t line, const std::string& fmt, Args&& ...arg)
            {
                doLog(Level::value::ERROR, filename, line, fmt, std::forward<Args>(arg)...);
            }

            template<typename ...Args>
            void _fatal(const std::string& filename, size_t line, const std::string& fmt, Args&& ...arg)
            {
                doLog(Level::value::FATAL, filename, line, fmt, std::forward<Args>(arg)...);
            }

        private:
            // 完成日志对象的初始化，接着进行格式化，最后实现日志落地
            template<typename ...Args>
            void doLog(Level::value level, const std::string& filename, size_t line, const std::string& fmt, Args&& ...arg)
            {
                // 1. 判断日志等级是否允许输出
                if(level < _limitLevel) { return; }

                // 2. 对fmt格式化字符串和不定参进行组织，形成日志消息
                char buffer[1024];
                snprintf(buffer, sizeof(buffer), fmt.c_str(), std::forward<Args>(arg)...);
                

                // 3. 初始化日志消息对象
                LogMsg message(filename, line, level, _loggerName, buffer);

                // 4. 格式化日志消息
                std::string formatted_str = _formatter->format(message);

                // 5. 落地日志消息
                log(formatted_str.c_str(), formatted_str.size());
            }

        protected:
            // 使用抽象接口实现日志落地，派生出同步日志器和异步日志器
            virtual void log(const char* msg, size_t len) = 0;

        protected:
            std::mutex _mtx;
            std::string _loggerName;  // 日志器名称
            std::atomic<Level::value> _limitLevel;  // 输出日志等级限制
            Formatter::ptr _formatter;  // 日志格式化器
            std::vector<LogSink::ptr> _sinks;  // 一个日志器可以有多个落地
        };

        // 定义重设日志器的类，并将其声明为Logger的友元类，便于外界通过函数调用重设日志器参数
        class ResetLogger
        {
        public:
            ResetLogger(const Logger::ptr& logger) : _logger(logger) {}

            // 重设日志器的名称
            void resetLoggerName(const std::string& loggerName)
            {
                _logger->_loggerName = loggerName;
            }
            // 重设日志器的输出等级
            void resetLimitLevel(Level::value level)
            {
                _logger->_limitLevel = level;
            }
            // 重设日志器的格式化器，设定日志格式
            void resetFormatter(const Formatter::ptr& formatter)
            {
                _logger->_formatter = formatter;
            }
            // 重设日志器的落地方向
            void resetSinks(const std::vector<LogSink::ptr>& sinks)
            {
                _logger->_sinks.clear();
                _logger->_sinks = sinks;
            }

        private:
            Logger::ptr _logger;  // 日志器对象
        };

        // 同步日志器
        class SyncLogger : public Logger
        {
        public:
            SyncLogger(const std::string& loggerName,
                       Level::value level,
                       const Formatter::ptr& formatter,
                        const std::vector<LogSink::ptr>& sinks)
                : Logger(loggerName, level, formatter, sinks) {}

        protected:
            // 实现日志落地的抽象接口
            void log(const char* msg, size_t len) override
            {
                std::lock_guard<std::mutex> lock(_mtx);
                if(_sinks.empty()) { return; }
                for(const auto& sink : _sinks)
                {
                    sink->log(msg, len);
                }
            }
        };

        // 异步日志器
        class AsyncLogger : public Logger
        {
        public:
            AsyncLogger(const std::string& loggerName,
                        Level::value level,
                        const Formatter::ptr& formatter,
                        const std::vector<LogSink::ptr>& sinks,
                        const AsyncSafeType looper_type)
                : Logger(loggerName, level, formatter, sinks), 
                  _looper(std::make_shared<AsyncLooper>(
                    [this](const Buffer::ptr& buffer) { this->realLog(buffer); }, 
                    looper_type)) {}

            void log(const char* msg, size_t len) override  // 将日志先存到缓冲区中
            {
                _looper->push(msg, len);
            }

            void realLog(const Buffer::ptr& buffer)  // 实际落地 （缓冲区->落地方向）
            {
                // 单执行流（缓冲区->落地方向），无需加锁
                if(_sinks.empty()) { return; }
                for(const auto& sink : _sinks)
                {
                    sink->log(buffer->begin(), buffer->readAbleSize());
                }
            }
        
        private:
            AsyncLooper::ptr _looper;  // 异步日志落地器
        };

        
        // 利用建造者模式进行日志器的创建
        enum class LoggerType
        {
            LOGGER_SYNC,
            LOGGER_ASYNC
        };
        class LoggerBulider
        {
        public:
            using ptr = std::shared_ptr<LoggerBulider>;

            LoggerBulider(): _loggerType(LoggerType::LOGGER_SYNC),  // 默认同步日志器
                            _limitLevel(Level::value::DEBUG),
                            _looperType(AsyncSafeType::ASYNC_SAFE) {}
            ~LoggerBulider() = default;
        public:
            void bulidLoggerType(LoggerType loggerType)
            {
                _loggerType = loggerType;
            }
            void bulidLoggerName(const std::string& loggerName)
            {
                _loggerName = loggerName;
            }
            void bulidLimitLevel(Level::value level)
            {
                _limitLevel = level;
            }
            void bulidFormatter(const std::string& pattern)
            {
                _formatter = std::make_shared<Formatter>(pattern);
            }
            template<typename SinkType, typename ...Args>
            void bulidSink(Args&&... args)
            {
                _sinks.emplace_back(SinkFactory::create<SinkType>(std::forward<Args>(args)...));
            }
            void bulidEnableAsyncUnSafe()
            {
                _looperType = AsyncSafeType::ASYNC_UNSAFE;
            }

            virtual Logger::ptr bulid()  // 虚函数，派生出具体的日志器, 子类（全局日志器、局部日志器）都有这部分公用代码
            {
                assert(_loggerName.empty() == false);
                if(_formatter == nullptr)
                {
                    _formatter = std::make_shared<Formatter>();
                }
                if(_sinks.empty())
                {
                    // 默认利用stdout落地到控制台文件
                    _sinks.emplace_back(std::make_shared<StdoutSink>());
                }

                // TODO -- 异步日志器
                if(_loggerType == LoggerType::LOGGER_ASYNC)
                {
                    return std::make_shared<AsyncLogger>(_loggerName, _limitLevel, _formatter, _sinks, _looperType);
                }
                // 同步日志器
                return std::make_shared<SyncLogger>(_loggerName, _limitLevel, _formatter, _sinks);
            }
        protected:
            LoggerType _loggerType;  // 同步or异步日志器
            std::string _loggerName;
            Level::value _limitLevel;
            Formatter::ptr _formatter;
            std::vector<LogSink::ptr> _sinks;
            AsyncSafeType _looperType;
        };
        // 派生出局部or全局日志器
        // 局部日志构建器
        class LocalLoggerBulider : public LoggerBulider
        {
        public:
            Logger::ptr bulid() override
            {
                return LoggerBulider::bulid();
            }
        };


        class LoggerManager
        {
        public:
            static LoggerManager& getInstance()
            {
                // c++11 线程安全的懒汉模式, 编译层面实现线程安全
                static LoggerManager instance;
                return instance;
            }

            void addLogger(const Logger::ptr& logger)
            {
                // 由于hasLogger是加锁的, 所以这里需要在lock_guard前面判断，否则会死锁
                if(hasLogger(logger->getLoggerName())) { return; }

                std::lock_guard<std::mutex> lock(_mtx);
                // 同名存在直接退出，避免[]操作符对原来Logger进行覆盖
                _loggers[logger->getLoggerName()] = logger;  
            }

            bool hasLogger(const std::string& loggerName)
            {
                std::lock_guard<std::mutex> lock(_mtx);
                return _loggers.find(loggerName) != _loggers.end();
            }

            Logger::ptr getLogger(const std::string& loggerName)
            {
                std::lock_guard<std::mutex> lock(_mtx);
                auto it = _loggers.find(loggerName);
                if(it == _loggers.end()) { return nullptr; }
                return it->second;
            }

            Logger::ptr getDefaultLogger() const
            {
                return _default_logger;
            }

        private:
            LoggerManager()
            {
                // 注意这里的构造者只能是LocalLoggerBulider，如果设置为GlobalLoggerBulider，则会导致依赖循环
                std::unique_ptr<LoggerBulider> bulider = std::make_unique<LocalLoggerBulider>();
                bulider->bulidLoggerName("default-logger");
                // 其余参数设置默认值
                _default_logger = bulider->bulid();

                _loggers["default-logger"] = _default_logger;
            }

            LoggerManager(const LoggerManager&) = delete;
            LoggerManager& operator=(const LoggerManager&) = delete;

        private:
            std::mutex _mtx;
            Logger::ptr _default_logger;
            std::unordered_map<std::string, Logger::ptr> _loggers;
        };


        class GlobalLoggerBulider : public LoggerBulider
        {
        public:
            Logger::ptr bulid() override
            {
                auto logger = LoggerBulider::bulid();
                // 注册到全局日志管理器
                LoggerManager::getInstance().addLogger(logger);
                return logger;
            }

        };
    }
}
#endif // LOGGER_HPP